﻿using DotNetCore.CAP;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Ongoal.Quotation.CapExtensions;
using Ongoal.Quotation.Model;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Ongoal.Quotation.Cap
{
    /// <summary>
    /// 消息发布器
    /// </summary>
    public class MessagePublisher : IMessagePublisher
    {
        private ICapPublisher? _capPublisher;
        public CapConfigModel _capConfig = null;

        /// <summary>
        /// 构造器
        /// </summary>
        /// <param name="capPublisher"></param>
        public MessagePublisher(ICapPublisher? capPublisher, IConfiguration config)
        {
            _capPublisher = capPublisher;
            _capConfig = GetCapConfig(config);
        }

        public IServiceProvider ServiceProvider
        {
            get
            {
                if (_capPublisher == null)
                    return null;

                return _capPublisher.ServiceProvider;
            }
        }

        /// <summary>
        /// CAP transaction context object
        /// </summary>
        public ICapTransaction? Transaction
        {
            get
            {
                if (_capPublisher == null)
                    return null;
                return _capPublisher.Transaction;
            }
            set
            {
                if (_capPublisher != null)
                    _capPublisher.Transaction = value;
            }
        }


        /// <summary>
        /// Asynchronous publish an object message.
        /// </summary>
        /// <param name="name">the topic name or exchange router key.</param>
        /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
        /// <param name="callbackName">callback subscriber name</param>
        /// <param name="cancellationToken"></param>
        public async Task PublishAsync<T>(string name, T? contentObj, string? callbackName = null,
            CancellationToken cancellationToken = default)
        {
            if (_capPublisher == null)
                return;
            if (_capConfig?.Enable == true)
                await _capPublisher.PublishAsync(name, contentObj, callbackName, cancellationToken);
        }
        /// <summary>
        /// Asynchronous publish an object message.
        /// </summary>
        /// <param name="name">the topic name or exchange router key.</param>
        /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
        /// <param name="cancellationToken"></param>
        public async Task PublishWithDefaultCallbackAsync<T>(string name, T? contentObj,
            CancellationToken cancellationToken = default)
        {
            if (_capPublisher == null)
                return;
            string callbackName = SubscribeConstantBase.DefaultCallback;
            if (_capConfig?.Enable == true)
                await _capPublisher.PublishAsync(name, contentObj, callbackName, cancellationToken);
        }


        /// <summary>
        /// Asynchronous publish an object message with custom headers
        /// </summary>
        /// <typeparam name="T">content object</typeparam>
        /// <param name="name">the topic name or exchange router key.</param>
        /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
        /// <param name="headers">message additional headers.</param>
        /// <param name="cancellationToken"></param>
        public async Task PublishAsync<T>(string name, T? contentObj, IDictionary<string, string?> headers,
            CancellationToken cancellationToken = default)
        {
            if (_capPublisher == null)
                return;
            if (_capConfig?.Enable == true)
                await _capPublisher.PublishAsync(name, contentObj, headers, cancellationToken);
        }

        /// <summary>
        /// Publish an object message.
        /// </summary>
        /// <param name="name">the topic name or exchange router key.</param>
        /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
        /// <param name="callbackName">callback subscriber name</param>
        public void Publish<T>(string name, T? contentObj, string? callbackName = null)
        {
            if (_capConfig?.Enable == true)
                _capPublisher?.Publish(name, contentObj, callbackName);
        }
        /// <summary>
        /// Publish an object message.
        /// </summary>
        /// <param name="name">the topic name or exchange router key.</param>
        /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
        public void PublishWithDefaultCallback<T>(string name, T? contentObj)
        {
            string callbackName = SubscribeConstantBase.DefaultCallback;
            if (_capConfig?.Enable == true)
                _capPublisher?.Publish(name, contentObj, callbackName);
        }

        /// <summary>
        /// Publish an object message.
        /// </summary>
        /// <param name="name">the topic name or exchange router key.</param>
        /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
        /// <param name="headers">message additional headers.</param>
        public void Publish<T>(string name, T? contentObj, IDictionary<string, string?> headers)
        {
            if (_capConfig?.Enable == true)
                _capPublisher?.Publish(name, contentObj, headers);
        }

        /// <summary>
        /// Asynchronous schedule a message to be published at the feature time with headers.
        /// </summary>
        /// <param name="delayTime">The delay for message to published.</param>
        /// <param name="name">The topic name or exchange router key.</param>
        /// <param name="contentObj">Message body content, that will be serialized. (can be null)</param>
        /// <param name="headers">message additional headers.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public async Task PublishDelayAsync<T>(TimeSpan delayTime, string name, T? contentObj, IDictionary<string, string?> headers, CancellationToken cancellationToken = default)
        {
            if (_capPublisher == null)
                return;
            if (_capConfig?.Enable == true)
                await _capPublisher.PublishDelayAsync(delayTime, name, contentObj, headers, cancellationToken);
        }

        /// <summary>
        /// Asynchronous schedule a message to be published at the feature time.
        /// </summary>
        /// <param name="delayTime">The delay for message to published.</param>
        /// <param name="name">The topic name or exchange router key.</param>
        /// <param name="contentObj">Message body content, that will be serialized. (can be null)</param>
        /// <param name="callbackName">Callback subscriber name.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public async Task PublishDelayAsync<T>(TimeSpan delayTime, string name, T? contentObj, string? callbackName = null, CancellationToken cancellationToken = default)
        {
            if (_capPublisher == null)
                return;
            if (_capConfig?.Enable == true)
                await _capPublisher.PublishDelayAsync(delayTime, name, contentObj, callbackName, cancellationToken);
        }

        /// <summary>
        /// Asynchronous schedule a message to be published at the feature time.
        /// </summary>
        /// <param name="delayTime">The delay for message to published.</param>
        /// <param name="name">The topic name or exchange router key.</param>
        /// <param name="contentObj">Message body content, that will be serialized. (can be null)</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public async Task PublishDelayWithDefaultCallbackAsync<T>(TimeSpan delayTime, string name, T? contentObj, CancellationToken cancellationToken = default)
        {
            if (_capPublisher == null)
                return;
            string callbackName = SubscribeConstantBase.DefaultCallback;
            if (_capConfig?.Enable == true)
                await _capPublisher.PublishDelayAsync(delayTime, name, contentObj, callbackName, cancellationToken);
        }

        /// <summary>
        /// Schedule a message to be published at the feature time.
        /// </summary>
        /// <param name="delayTime">The delay for message to published.</param>
        /// <param name="name">The topic name or exchange router key.</param>
        /// <param name="contentObj">Message body content, that will be serialized. (can be null)</param>
        /// <param name="headers">message additional headers.</param>
        public void PublishDelay<T>(TimeSpan delayTime, string name, T? contentObj, IDictionary<string, string?> headers)
        {
            if (_capConfig?.Enable == true)
                _capPublisher?.PublishDelay(delayTime, name, contentObj, headers);
        }

        /// <summary>
        /// Schedule a message to be published at the feature time.
        /// </summary>
        /// <param name="delayTime">The delay for message to published.</param>
        /// <param name="name">The topic name or exchange router key.</param>
        /// <param name="contentObj">Message body content, that will be serialized. (can be null)</param>
        /// <param name="callbackName">Callback subscriber name.</param>
        public void PublishDelay<T>(TimeSpan delayTime, string name, T? contentObj, string? callbackName = null)
        {
            if (_capConfig?.Enable == true)
                _capPublisher?.PublishDelay(delayTime, name, contentObj, callbackName);
        }
        /// <summary>
        /// Schedule a message to be published at the feature time.
        /// </summary>
        /// <param name="delayTime">The delay for message to published.</param>
        /// <param name="name">The topic name or exchange router key.</param>
        /// <param name="contentObj">Message body content, that will be serialized. (can be null)</param>
        public void PublishDelayWithDefaultCallback<T>(TimeSpan delayTime, string name, T? contentObj)
        {
            string callbackName = SubscribeConstantBase.DefaultCallback;
            if (_capConfig?.Enable == true)
                _capPublisher?.PublishDelay(delayTime, name, contentObj, callbackName);
        }
        /// <summary>
        /// 事务类操作数据库和分布式事务发布数据专用方法（微服务数据库未分离时调用方法）
        /// </summary>
        /// <param name="ado"></param>
        /// <param name="func"></param>
        public async Task<bool> ExecuteDBAndPublishWithTranAsync(IAdo ado, Func<Task<bool>> func)
        {
            bool result = false;
            if (func == null)
            {
                return result;
            }
            if (ado.Connection.State == ConnectionState.Closed) ado.Connection.Open();

            if (_capConfig?.Enable == true)
            {
                var tran = this.BeginTransaction(ado, false);
                try
                {
                    result = await func();
                    if (!result)
                    {
                        tran.Rollback();
                        return result;
                    }
                    else
                    {
                        tran.Commit();
                        return result;
                    }

                }
                catch (Exception exp)
                {
                    tran.Rollback();
                    throw exp;
                }
            }
            else
            {
                ado.BeginTran();
                try
                {
                    result = await func();
                    if (!result)
                    {
                        ado.RollbackTran();
                        return result;
                    }
                    else
                    {
                        ado.CommitTran();
                        return result;
                    }

                }
                catch (Exception exp)
                {
                    ado.RollbackTran();
                    throw exp;
                }
            }
        }

        /// <summary>
        /// 事务类操作数据库和分布式事务发布数据专用方法（微服务数据库未分离时调用方法）
        /// </summary>
        /// <param name="ado"></param>
        /// <param name="func"></param>
        public bool ExecuteDBAndPublish(IAdo ado, Func<bool> func)
        {
            bool result = false;
            if (func == null)
            {
                return result;
            }
            if (ado.Connection.State == ConnectionState.Closed) ado.Connection.Open();

            if (_capConfig?.Enable == true)
            {
                var tran = this.BeginTransaction(ado, false);
                try
                {
                    result = func();
                    if (!result)
                    {
                        tran.Rollback();
                        return result;
                    }
                    else
                    {
                        tran.Commit();
                        return result;
                    }

                }
                catch (Exception exp)
                {
                    tran.Rollback();
                    throw exp;
                }
            }
            else
            {
                ado.BeginTran();
                try
                {
                    result = func();
                    if (!result)
                    {
                        ado.RollbackTran();
                        return result;
                    }
                    else
                    {
                        ado.CommitTran();
                        return result;
                    }

                }
                catch (Exception exp)
                {
                    ado.RollbackTran();
                    throw exp;
                }
            }
        }


        private CapConfigModel? GetCapConfig(IConfiguration config)
        {
            var dbCapConfigStr = config["CapConfig"];
            CapConfigModel? cfg = null;
            try
            {
                if (!string.IsNullOrEmpty(dbCapConfigStr))
                    cfg = JsonConvert.DeserializeObject<CapConfigModel>(dbCapConfigStr);
                else
                    cfg = config.GetSection("CapConfig").Get<CapConfigModel>();
            }
            catch
            {
                cfg = new CapConfigModel() { Enable = false };
            }

            return cfg;

        }
    }
}
